﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using Storage;
using Storage.EntityModels;
using System.Data.Linq;
using Stats.Objects;
using System.Transactions;

namespace Stats
{

    /// <summary>
    /// The class contains methods created to support the programming of the rest of the library. only utility methods are
    /// contained in here
    /// </summary>
    public class AuxMethods
    {

        #region system methods

        /// <summary>
        /// returns the not-banned registered users
        /// </summary>
        /// <returns>the number of not-banned registered users</returns>
        public static int getRegisteredUsersCountOperation()
        {
            int usersCount = 0;

            try
            {
                TripLoaDataContext db = StorageManager.Instance.Entities;
                
                usersCount =
                    (from users in db.Users
                     where users.BookedIn == true
                     select users).Count();
            }
            catch (InvalidOperationException) { usersCount = 0; }
            catch (NullReferenceException) { usersCount = 0; }
            catch (System.Data.SqlClient.SqlException) { usersCount = 0; }

            return usersCount;
        }

        /// <summary>
        /// returns the number of non-draft created trips
        /// </summary>
        /// <returns>the number of trips</returns>
        public static int getCreatedTripsCountOperation()
        {
            int tripsCount = 0;

            try
            {
                TripLoaDataContext db = StorageManager.Instance.Entities;
                tripsCount =
                    (from trips in db.Trips
                     where trips.Draft == false
                     select trips).Count();
            }
            catch (InvalidOperationException) { tripsCount = 0; }
            catch (NullReferenceException) { tripsCount = 0; }
            catch (System.Data.SqlClient.SqlException) { tripsCount = 0; }

            return tripsCount;
        }

        /// <summary>
        /// returns the last n registered users
        /// </summary>
        /// <param name="usersToReturnNumber">the number of registered users to return</param>
        /// <returns>a list of registered users</returns>
        public static List<RegisteredUser> getLastNRegisteredUsersOperation(int usersToReturnNumber)
        {
            List<RegisteredUser> lastNregisteredUserList = new List<RegisteredUser>();

            int usersBookedInCount = getRegisteredUsersCountOperation();

            if (usersBookedInCount != 0)
            {
                if (usersBookedInCount < usersToReturnNumber) { usersToReturnNumber = usersBookedInCount; }

                try
                {
                    TripLoaDataContext db = StorageManager.Instance.Entities;

                    var lastNRegisteredUsers =
                        (from userEntry in db.Users
                         where userEntry.BookedIn == true
                         select new RegisteredUser(userEntry.Id, userEntry.Username, userEntry.Gender)).Skip(usersBookedInCount - usersToReturnNumber);

                    foreach (var registeredUser in lastNRegisteredUsers)
                    {
                        if (registeredUser != null && registeredUser.Username != null)
                        {
                            lastNregisteredUserList.Add(registeredUser);
                        }
                    }

                }
                catch (InvalidOperationException) { lastNregisteredUserList = new List<RegisteredUser>(); }
                catch (NullReferenceException) { lastNregisteredUserList = new List<RegisteredUser>(); }
                catch (InvalidCastException) { lastNregisteredUserList = new List<RegisteredUser>(); }
                catch (ArgumentOutOfRangeException) { lastNregisteredUserList = new List<RegisteredUser>(); }
                catch (System.Data.SqlClient.SqlException) { lastNregisteredUserList = new List<RegisteredUser>(); }

            }

            return lastNregisteredUserList;

        }

        /// <summary>
        /// returns the last n created trips 
        /// </summary>
        /// <param name="n">the number of trips to return</param>
        /// <returns>a list of TripProperties</returns>
        public static List<TripProperties> getLastNCreatedTripsOperation(int n)
        {
            List<TripProperties> lastTrips = new List<TripProperties>();

            int tripsCount = getCreatedTripsCountOperation();

            if (tripsCount != 0)
            {
                if (tripsCount < n) { n = tripsCount; }
            
                TripLoaDataContext db = StorageManager.Instance.Entities;

                try
                {
                    var lastNCreatedTrips =
                        (from tripEntry in db.Trips
                         where tripEntry.Draft == false
                         select new { ID = tripEntry.Id }).Skip(tripsCount - n);

                    foreach (var tripEntry in lastNCreatedTrips)
                    {
                        TripProperties trInfo = AuxTripMethods.getTripProperties(tripEntry.ID);

                        if (trInfo != null)
                        {
                            lastTrips.Add(trInfo);
                            //System.Console.WriteLine(trInfo.DepartureDate + " TRIP " + trInfo.ID + " FROM " + trInfo.OriginCity + trInfo.OriginAddress);
                            //System.Console.WriteLine(" TO " + trInfo.DestinationCity + trInfo.DestinationAddress);
                            //System.Console.WriteLine();
                        }
                    }
                }
                catch (InvalidOperationException) { lastTrips = new List<TripProperties>(); }
                catch (NullReferenceException) { lastTrips = new List<TripProperties>(); }
                catch (ArgumentOutOfRangeException) { lastTrips = new List<TripProperties>(); }
                catch (System.Data.SqlClient.SqlException) { lastTrips = new List<TripProperties>(); }
            }

            return lastTrips;
        }

        /// <summary>
        /// This method cleans the pending feedbacks table in db removing the expired pending feedbacks...
        /// A pending feedback expires when are passed displayPendingFeedbackAfterDays+PendingFeedback_ExpireDays days from the related trip departure date.
        /// </summary>
        public static void cleanExpiredPendingFeedbacksOperation()
        {
            try
            {
                using (TransactionScope t1 = new TransactionScope())
                {

                    TripLoaDataContext db = StorageManager.Instance.Entities;

                    DateTime expiringDate = DateTime.Today.AddDays(-StatsModule.PendingFeedback_ExpireDays);

                    var expiredPendingFeedbacks =
                        from pendingFeedback in db.PendingFeedbacks
                        where pendingFeedback.Date < expiringDate
                        select pendingFeedback;

                    db.PendingFeedbacks.DeleteAllOnSubmit(expiredPendingFeedbacks);
                    db.SubmitChanges();

                    t1.Complete();

                }

            }
            catch (InvalidOperationException) { }
            catch (NullReferenceException) { }
            catch (System.Data.SqlClient.SqlException) { }
            catch (TransactionAbortedException) { }
        }

        ///// <summary>
        ///// clears the expired feedback on a cancelled trip
        ///// </summary>
        ///// <param name="tripID">the trip id</param>
        ///// <returns>false on failed operation, true otherwise</returns>
        //public static bool deletePendingFeedbacksOnCancellingTripOperation(int tripID)
        //{
        //    bool cancellingPendingFeedbacksOK = true;

        //    try
        //    {
        //        TripLoaDataContext db = StorageManager.Instance.Entities;

        //        var tripPendingFeedbacks =
        //            from pendingFeedback in db.PendingFeedbacks
        //            where pendingFeedback.TripId == tripID
        //            select pendingFeedback;

        //        db.PendingFeedbacks.DeleteAllOnSubmit(tripPendingFeedbacks);
        //        db.SubmitChanges();

        //    }
        //    catch (InvalidOperationException) { cancellingPendingFeedbacksOK = false; }
        //    catch (NullReferenceException) { cancellingPendingFeedbacksOK = false; }
        //    catch (System.Data.SqlClient.SqlException) { cancellingPendingFeedbacksOK = false; }
        //    catch (TransactionAbortedException) {cancellingPendingFeedbacksOK = false; }

        //    return cancellingPendingFeedbacksOK;

        //}
        
        /// <summary>
        /// calculates how many health dangerous emissions have been avoided by TripLOA since the moment of the invocation
        /// </summary>
        /// <returns>the quantity of saved emissions in quintals of CO2</returns>
        public static double getTotalEmissionsSaved()
        {
            double totalEmissionsSaved = 0;

            try
            {
                TripLoaDataContext db = StorageManager.Instance.Entities;

                totalEmissionsSaved = (double)
                    (from dailyEntry in db.DailyStatistics
                     select dailyEntry.PollutionSaved).Sum();
            }
            catch (InvalidOperationException) { totalEmissionsSaved = 0; }
            catch (NullReferenceException) { totalEmissionsSaved = 0; }
            catch (System.Data.SqlClient.SqlException) { totalEmissionsSaved = 0; }

            return AuxMethods.TruncateTo3Decimals(totalEmissionsSaved);
        }

        /// <summary>
        /// calculates the money that the use of TripLOA have been saved
        /// </summary>
        /// <returns>an average of saved fuel</returns>
        /// TODO this methods has greats possibility of improvements, changing the cost of fuel per liter from a constant to a variable updated every
        /// day (maybe with some RSS), calculate considering the kind of fuel (patrol/diesel/gas) looking at the cars and so on...
        public static double getTotalMoneySaved()
        {
            return getTotalFuelSaved() * StatsModule.FuelCostPerLiterReference;
        }

        /// <summary>
        /// calculates the totality of saved fuel using TripLOA.
        /// </summary>
        /// <returns>the fuel saved in liters</returns>
        public static double getTotalFuelSaved()
        {
            double totalFuelSaved = 0;

            try
            {
                TripLoaDataContext db = StorageManager.Instance.Entities;

                totalFuelSaved = (double)
                    (from dailyEntry in db.DailyStatistics
                     select dailyEntry.FuelSaved).Sum();
            }
            catch (InvalidOperationException) { totalFuelSaved = 0; }
            catch (NullReferenceException) { totalFuelSaved = 0; }
            catch (System.Data.SqlClient.SqlException) { totalFuelSaved = 0; }

            return AuxMethods.TruncateTo3Decimals(totalFuelSaved);
        }

        /// <summary>
        /// Auxiliay method for the calculation of the total km saved using TripLOA
        /// </summary>
        /// <returns>The number of kilometers people haven't ride alone thank to the system</returns>
        public static double getTotalKmSaved()
        {
            double totalKmSaved = 0;

            try
            {
                TripLoaDataContext db = StorageManager.Instance.Entities;

                double stageKm = 0;

                int psnCount = 0;

                foreach (var stageEntry in db.Stages)
                {
                    stageKm = AuxDistanceMethods.getStageKm(stageEntry.Id);

                    psnCount = stageEntry.BusySeats;  //assuming busy seats are occupied by non-driving passengers

                    totalKmSaved += stageKm * psnCount;  //kilometers per passenger
                }

            }
            catch (InvalidOperationException) { totalKmSaved = 0; }
            catch (NullReferenceException) { totalKmSaved = 0; }
            catch (System.Data.SqlClient.SqlException) { totalKmSaved = 0; }

            return totalKmSaved;

        }
        
        #endregion

        /// <summary>
        /// A simple, useful and smart utility method that truncate a double to 3 decimals after the comma
        /// </summary>
        /// <param name="d">the double</param>
        /// <returns>the trouncated double</returns>
        public static double TruncateTo3Decimals(double d)
        {
            double p = Math.Pow(10, (double) 3);
            return ((int)(d * p)) / p;
        }


    
    }   
}
